/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 *
 * $Revision: 10874 $
 * $Id: workspaceloader.cpp 10874 2016-07-16 20:00:28Z jenslody $
 * $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-17.xx/src/sdk/workspaceloader.cpp $
 */

#include "sdk_precomp.h"

#ifndef CB_PRECOMP
    #include <wx/confbase.h>
    #include <wx/fileconf.h>
    #include <wx/intl.h>
    #include <wx/string.h>

    #include "workspaceloader.h"

    #include "manager.h"
    //#include "FileManager.h"
    #include "configmanager.h"
    #include "projectmanager.h"
    #include "logmanager.h"
    #include "cbproject.h"
    #include "globals.h"
    #include "cbworkspace.h"
    #include "editormanager.h"
    #include "cbauibook.h"
#endif



#include "annoyingdialog.h"
#include <tinyxml.h>
#include "tinywxuni.h"

WorkspaceLoader::WorkspaceLoader()
{
    // 构造函数
}

WorkspaceLoader::~WorkspaceLoader()
{
    // 析构函数
}

inline ProjectManager* GetpMan() { return Manager::Get()->GetProjectManager(); }
inline LogManager* GetpMsg() { return Manager::Get()->GetLogManager(); }

#include <wx/intl.h>

bool WorkspaceLoader::Open(const wxString& filename, wxString& Title)
{
    //Manager::Get()->GetLogManager()->DebugLog(wxT('-----载入工作区'));
    TiXmlDocument doc;
    Manager::Get()->GetLogManager()->DebugLog(F(wxT("------->开始载入%s工作区文档"),filename.wx_str()));
    if (!TinyXML::LoadDocument(filename, &doc))
        return false;
    Manager::Get()->GetLogManager()->DebugLog(F(wxT("------->完成%s工作区文档载入"),filename.wx_str()));

    //项目管理器和日志管理器
    if (!GetpMan() || !GetpMsg())
        return false;

/**
*如果工作区文档存在则应该设置(插件的)文件管理器的根目录,如果文件管理器插件已经载入并激活的话
cbAuiNotebook *nb;
cbPlugin *fmPlugins;
nb=GetpMan()->GetUI().GetNotebook();
for (size_t i = 0; i < nb->GetPageCount() ; ++i) {
GetpMsg()->DebugLog(F(wxT("PageName:%s->PageLabel:%s"),nb->GetPage(i)->GetName().wx_str(),nb->GetLabel().wx_str()));

//if(nb->GetPage(i)->GetLabel())
}
//FileManagerPlugin
//nb->GetPage()
*/
    // BUG: Race condition. to be fixed by Rick.
    // If I click close AFTER pMan and pMsg are calculated,
    // I get a segfault.
    // I modified classes projectmanager and logmanager,
    // so that when self==NULL, they do nothing
    // (constructors, destructors and static functions excempted from this)
    // This way, we'll use the *manager::Get() functions to check for nulls.
    //
    // 错误:竞速条件状况.由瑞克修正.
    // 如果在计算pMan和pMsg之后单击关闭按钮(close),则会得到分段故障(segfault).
    // 我修改了项目管理器(ProjectManager)和日志管理器日志管理器(LogManager)类,
    // 当self==NULL时,它们什么也不做(构造函数/析构函数和静态函数除外),
    // 这样,我们将使用*manager::Get()函数来检查NULL.
    GetpMsg()->DebugLog(F(wxT("------->解释和配置%s工作区."),filename.wx_str()));
    TiXmlElement* root = doc.FirstChildElement("CodeBlocks_workspace_file");
    if (!root)
    {
        // old tag
        // 旧标签
        root = doc.FirstChildElement("Code::Blocks_workspace_file");
        if (!root)
        {
            GetpMsg()->DebugLog(wxT("不是有效的Code::Blocks工作区文件..."));
            return false;
        }
    }
    TiXmlElement* wksp = root->FirstChildElement("Workspace");
    if (!wksp)
    {
        GetpMsg()->DebugLog(wxT("文件中没有工作区(Workspace)元素..."));
        return false;
    }

    // Conversion to unicode is automatic (see wxString::operator= )
    // 自动转换为unicode(请参见wxString::operator=)

    Title = cbC2U(wksp->Attribute("title"));
    GetpMsg()->DebugLog(F(wxT("------->完成%s工作区解释和配置,开始处理工作区项目..."),filename.wx_str()));

    //工作区项目
    TiXmlElement* proj = wksp->FirstChildElement("Project");
    if (!proj)
    {
        GetpMsg()->DebugLog(wxT("工作区文件不包含项目..."));
        return false;
    }

    // first loop to load projects
    // 加载项目的第一个循环
    while (proj)
    {
        //正在退出应用...返回
        if (Manager::IsAppShuttingDown() || !GetpMan() || !GetpMsg())
            return false;
        wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
        if (projectFilename.IsEmpty())
        {
            GetpMsg()->DebugLog(wxT("项目(Project)节点存在,但没有文件名?!?"));
        }
        else
        {
            wxFileName fname(projectFilename);
            wxFileName wfname(filename);
            fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
            // don't activate it
            // 不要激活它
            GetpMsg()->DebugLog(F(wxT("------->载入%s项目."),fname.GetName().wx_str()));
            cbProject* pProject = GetpMan()->LoadProject(fname.GetFullPath(), false);
            if (!pProject)
            {
                cbMessageBox(wxT("无法打开") + projectFilename,
                 wxT("打开工作区") + filename, wxICON_WARNING);
            }
        }
        proj = proj->NextSiblingElement("Project");
    }

    GetpMsg()->DebugLog(F(wxT("------->完成%s工作区项目的载入."),filename.wx_str()));
    // second loop to setup dependencies
    // 设置依赖项的第二个循环,依赖也是一种项目
    proj = wksp->FirstChildElement("Project");
    while (proj)
    {
        cbProject* thisprj = nullptr;
        wxString projectFilename = UnixFilename(cbC2U(proj->Attribute("filename")));
        if (projectFilename.IsEmpty())
        {
            GetpMsg()->DebugLog(wxT("项目(Project)节点存在,但没有文件名?!?"));
            thisprj = nullptr;
        }
        else
        {
            //项目名
            wxFileName fname(projectFilename);
            //工作区名称
            wxFileName wfname(filename);
            fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
            thisprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
        }

        if (thisprj)
        {
            wxFileName prjfn(projectFilename);
            wxFileName wfname(filename);

            TiXmlElement* dep = proj->FirstChildElement("Depends");
            while (dep)
            {
                wxFileName fname( UnixFilename(cbC2U(dep->Attribute("filename"))) );
                wxFileName wfname(filename);
                fname.MakeAbsolute(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
                cbProject* depprj = Manager::Get()->GetProjectManager()->IsOpen(fname.GetFullPath());
                if (depprj) {

                    GetpMsg()->DebugLog(F(wxT("------->向%s项目添加依赖%s."),prjfn.GetName().wx_str(),fname.GetName().wx_str()));

                    Manager::Get()->GetProjectManager()->AddProjectDependency(thisprj, depprj);
                }
                dep = dep->NextSiblingElement("Depends");
            }
        }
        proj = proj->NextSiblingElement("Project");
    }
    GetpMsg()->DebugLog(wxT("------->完成工作区项目的依赖项载入."));
    //TODO:设置管理器面板的文件面板的的路径
    return true;
}

bool WorkspaceLoader::Save(const wxString& title, const wxString& filename)
{
    const char* ROOT_TAG = "CodeBlocks_workspace_file";

    TiXmlDocument doc;
    doc.SetCondenseWhiteSpace(false);
    doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
    TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
    if (!rootnode)
        return false;

    TiXmlElement* wksp = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("Workspace")));
    wksp->SetAttribute("title", cbU2C(title));

    ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
    for (unsigned int i = 0; i < arr->GetCount(); ++i)
    {
        cbProject* prj = arr->Item(i);

        wxFileName wfname(filename);
        wxFileName fname(prj->GetFilename());
        fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));

        TiXmlElement* node = static_cast<TiXmlElement*>(wksp->InsertEndChild(TiXmlElement("Project")));
        node->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );

        const ProjectsArray* deps = Manager::Get()->GetProjectManager()->GetDependenciesForProject(prj);
        if (deps && deps->GetCount())
        {
            for (size_t j = 0; j < deps->GetCount(); ++j)
            {
                prj = deps->Item(j);
                fname.Assign(prj->GetFilename());
                fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
                TiXmlElement* dnode = static_cast<TiXmlElement*>(node->InsertEndChild(TiXmlElement("Depends")));
                dnode->SetAttribute("filename", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
            }
        }
    }
    return cbSaveTinyXMLDocument(&doc, filename);
}

bool WorkspaceLoader::SaveLayout(const wxString& filename)
{
    Manager::Get()->GetLogManager()->DebugLog(wxT("-----载入工作区布局"));
    const char* ROOT_TAG = "CodeBlocks_workspace_layout_file";

    TiXmlDocument doc;
    doc.SetCondenseWhiteSpace(false);
    doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
    TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
    // Failed creating the root node of the workspace layout XML file?!
    // 创建工作区布局XML文件的根节点失败?!
    if (!rootnode){return false;}

    rootnode->InsertEndChild(TiXmlElement("FileVersion"));
    rootnode->FirstChildElement("FileVersion")->SetAttribute("major", WORKSPACE_LAYOUT_FILE_VERSION_MAJOR);
    rootnode->FirstChildElement("FileVersion")->SetAttribute("minor", WORKSPACE_LAYOUT_FILE_VERSION_MINOR);

    // active project
    // 激活项目
    ProjectManager *pm = Manager::Get()->GetProjectManager();
     // Could not access ProjectManager?!
     // 无法访问项目管理器?!
    if (!pm)
        return false;

    if (const cbProject *project = pm->GetActiveProject())
    {
        TiXmlElement *el =
            static_cast<TiXmlElement*>(
                rootnode->InsertEndChild( TiXmlElement("ActiveProject") ) );
        wxFileName wfname(filename);
        wxFileName fname( project->GetFilename() );
        fname.MakeRelativeTo(wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
        el->SetAttribute("path", cbU2C( UnixFilename(fname.GetFullPath(), wxPATH_UNIX) ) );
    }
    // else Workspace has no active project?!
    // preferred build target
    // 否则工作区没有活动项目?!
    // 首选生成目标

    if (const cbWorkspace* wsp = pm->GetWorkspace() )
    {
        const wxString preferredTarget = wsp->GetPreferredTarget();
        if ( ! preferredTarget.IsEmpty() )
        {
            TiXmlElement* el =
                static_cast<TiXmlElement*>(
                    rootnode->InsertEndChild( TiXmlElement("PreferredTarget") ) );
            el->SetAttribute("name", cbU2C(preferredTarget) );
        }
        // else Project has not preferred target.
        // 否则项目没有首选目标.
    }
    // else No workspace present to save.
    // 否则不存在要保存的工作区.

    if (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false))
    {
        TiXmlElement *el =
            static_cast<TiXmlElement*>(
                rootnode->InsertEndChild( TiXmlElement("EditorTabsLayout") ) );
        el->SetAttribute("layout", cbU2C( Manager::Get()->GetEditorManager()->GetNotebook()->SavePerspective() ));
    }
    // else ?!

    return cbSaveTinyXMLDocument(&doc, filename);
}

bool WorkspaceLoader::LoadLayout(const wxString& filename)
{
    TiXmlDocument doc;
    // Can't load XML file?!
    // 无法加载XML文件?!
    if ( ! TinyXML::LoadDocument(filename, &doc) )
        return false;

        // GetpMan or GetpMsg returns NULL?!
        // GetpMan或GetpMsg返回空?!
    if ( ! GetpMan() || ! GetpMsg() )
        return false;

    TiXmlElement* root = doc.FirstChildElement("CodeBlocks_workspace_layout_file");
    if (!root)
    {
        GetpMsg()->DebugLog(wxT("无法加载Code::Blocks工作区布局文件:文件无效."));
        return false;
    }

    int major = 0;
    int minor = 0;

    TiXmlElement* version = root->FirstChildElement("FileVersion");

    // don't show messages if we 're running a batch build (i.e. no gui)
    // 如果正在运行批处理生成,则不显示消息(即没有GUI)

    if (!Manager::IsBatchBuild() && version)
    {
        version->QueryIntAttribute("major", &major);
        version->QueryIntAttribute("minor", &minor);

        if (major >= WORKSPACE_LAYOUT_FILE_VERSION_MAJOR && minor > WORKSPACE_LAYOUT_FILE_VERSION_MINOR)
        {
            GetpMsg()->DebugLog(F(wxT("工作区布局文件版本大于%d%d.正在尝试加载..."), WORKSPACE_LAYOUT_FILE_VERSION_MAJOR, WORKSPACE_LAYOUT_FILE_VERSION_MINOR));
            AnnoyingDialog dlg(wxT("工作区布局文件格式较新/未知"),
                                F(wxT("此工作区布局文件是用较新版本的Code::Blocks保存的.\n"
                                "将尝试加载,但可能会看到意外的结果.\n"
                                "在这种情况下,请关闭工作区,删除%s并重新打开工作区."),filename.wx_str()),
                                wxART_WARNING,
                                AnnoyingDialog::OK);
            dlg.ShowModal();
        }
        else
        {
            // use one message for all changes
            // 对所有更改使用一条消息

            wxString msg;
            wxString warn_msg;

            if (major == 0 && minor == 0)
            {
                msg << wxT("0.0 (未翻译) 到 1.0:\n");
                msg << wxT("  * 保存编辑器窗格布局和顺序.\n");
                msg << wxT("\n");
            }

            if (!msg.IsEmpty())
            {
                msg.Prepend(wxString::Format(wxT("工作区布局文件格式比当前格式(即%d.%d)旧(即%d.%d).\n"
                                                "文件将在关闭时自动升级.\n"
                                                "但请阅读以下更改列表,因为其中一些更改可能无法自动转换现有(旧)设置.\n"
                                                "如果你不明白改变意味着什么,你可能不使用这个功能,所以你不必担心它.\n\n"
                                                "更改列表:\n"),
                                            major,
                                            minor,
                                            WORKSPACE_LAYOUT_FILE_VERSION_MAJOR,
                                            WORKSPACE_LAYOUT_FILE_VERSION_MINOR));
                AnnoyingDialog dlg(wxT("工作区布局文件格式已更改"),
                                    msg,
                                    wxART_INFORMATION,
                                    AnnoyingDialog::OK);
                dlg.ShowModal();
            }

            if (!warn_msg.IsEmpty())
            {
                warn_msg.Prepend(wxT("!!! 警告 !!!\n\n"));
                AnnoyingDialog dlg(wxT("工作区布局文件升级警告"),
                                    warn_msg,
                                    wxART_WARNING,
                                    AnnoyingDialog::OK);
                dlg.ShowModal();
            }
        }
    }

    // active project
    // 激活项目
    if (TiXmlElement* el = root->FirstChildElement("ActiveProject"))
    {
        wxFileName fname = cbC2U( el->Attribute("path") );
        wxFileName wfname(filename);
        fname.MakeAbsolute( wfname.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR) );
        cbProject *project = GetpMan()->IsOpen( fname.GetFullPath() );
        if (project)
        {
            GetpMan()->SetProject(project);
            Manager::Get()->GetLogManager()->DebugLog(F(wxT("项目%s已激活."), fname.GetFullPath().wx_str()));
        }
        else
            Manager::Get()->GetLogManager()->DebugLog(F(wxT("无法激活项目: %s"), fname.GetFullPath().wx_str()));
    }
    // else XML element 'ActiveProject' not found?!
    // 否则找不到XML元素"激活的项目(ActiveProject)"?!

    // preferred build target
    // 首选构建目标

    if (TiXmlElement* el = root->FirstChildElement("PreferredTarget"))
    {
        const wxString name = cbC2U(el->Attribute("name"));
        cbWorkspace *wsp = GetpMan()->GetWorkspace();
        if (wsp)
            wsp->SetPreferredTarget(name);
    }
    // else XML element 'PreferredTarget' not found?!
    // 否则找不到XML元素首选构建目标(PreferredTarget)?!

    if (   (major >= 1)
        && (Manager::Get()->GetConfigManager(_T("app"))->ReadBool(_T("/environment/enable_editor_layout"), false)) )
    {
        if (TiXmlElement* el = root->FirstChildElement("EditorTabsLayout"))
        {
            if (el->Attribute("layout"))
                Manager::Get()->GetEditorManager()->GetNotebook()->LoadPerspective(cbC2U(el->Attribute("layout")));
        }
    }

    return true;
}
